/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "SMAEncoder.h"

static const char sBase64Digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

CFStringRef SMAEncodeBase64FromData(CFAllocatorRef alloc, CFDataRef data)
{
   if (data == NULL) {
      return NULL;
   }
   
   CFIndex dataLength = CFDataGetLength(data);
   const UInt8* dataBytePtr = CFDataGetBytePtr(data);

   CFIndex encodedLength = (dataLength / 3) * 4;
   if ((dataLength % 3) != 0) {
      encodedLength += 4;
   }

   CFMutableStringRef string = CFStringCreateMutable(alloc, encodedLength);
   
   char buffer[5];
   
   while (dataLength >= 3)
   {
      buffer[0] = sBase64Digits[dataBytePtr[0] >> 2];
      buffer[1] = sBase64Digits[((dataBytePtr[0] << 4) & 0x30) | (dataBytePtr[1] >> 4)];
      buffer[2] = sBase64Digits[((dataBytePtr[1] << 2) & 0x3c) | (dataBytePtr[2] >> 6)];
      buffer[3] = sBase64Digits[dataBytePtr[2] & 0x3f];
      buffer[4] = 0x00;

      CFStringAppendCString(string, buffer, kCFStringEncodingASCII);

      dataBytePtr += 3;
      dataLength -= 3;
   }

   if (dataLength > 0)
   {
      char fragment = (dataBytePtr[0] << 4) & 0x30;
      if (dataLength > 1) {
         fragment |= dataBytePtr[1] >> 4;
      }
      
      buffer[0] = sBase64Digits[dataBytePtr[0] >> 2];
      buffer[1] = sBase64Digits[(int) fragment];
      buffer[2] = (dataLength < 2) ? '=' : sBase64Digits[(dataBytePtr[1] << 2) & 0x3c];
      buffer[3] = '=';
      buffer[4] = 0x00;
      
      CFStringAppendCString(string, buffer, kCFStringEncodingASCII);
   }

   return string;
}

static const char sHexDigits[] = "0123456789abcdef";

CFStringRef SMAEncodeHexFromData(CFAllocatorRef alloc, CFDataRef data)
{
   if (data == NULL) {
      return NULL;
   }

   const UInt8* p = CFDataGetBytePtr(data);
   CFIndex l = CFDataGetLength(data);

   CFMutableStringRef string = CFStringCreateMutable(alloc, CFDataGetLength(data) * 2);
   if (string != NULL) {
      for (CFIndex i = 0; i < l; i++) {
         char s[3];
         s[0] = sHexDigits[(p[i] & 0xf0) >> 4];
         s[1] = sHexDigits[(p[i] & 0x0f)];
         s[2] = 0x00;
         CFStringAppendCString(string, s, kCFStringEncodingASCII);
      }
   }

   return string;
}

CFStringRef SMAEncodeHexFromBytes(CFAllocatorRef alloc, const UInt8* data, CFIndex length)
{
   if (data == NULL) {
      return NULL;
   }

   CFMutableStringRef string = CFStringCreateMutable(alloc, length * 2);
   if (string != NULL) {
      for (CFIndex i = 0; i < length; i++) {
         char s[3];
         s[0] = sHexDigits[(data[i] & 0xf0) >> 4];
         s[1] = sHexDigits[(data[i] & 0x0f)];
         s[2] = 0x00;
         CFStringAppendCString(string, s, kCFStringEncodingASCII);
      }
   }

   return string;   
}

#if defined(SMAENCODER_TEST)
int main()
{
   char* s = "blue";
   CFShow(SMAEncodeBase64FromData(NULL, CFDataCreate(NULL, (UInt8*) s, 4)));
   CFShow(SMAEncodeHexFromData(NULL, CFDataCreate(NULL, (UInt8*) s, 4)));
   return 0;
}
#endif
